#pragma once

#include "Common.hpp"
#include "Log.hpp"
#include <memory>

using namespace LogModule;

const static int default_sockfd = -1;
const static int default_backlog = 16;

class Socket
{
public:
    virtual void SocketOrDie() = 0; // = 0, 不需要实现 
    virtual void BindOrDie(uint16_t port) = 0;
    virtual void ListenOrDie(int backlog) = 0;
    virtual void ConnectOrDie(std::string &server_ip, uint16_t server_port) = 0;
    virtual std::shared_ptr<Socket> Accept(InetAddr* client) = 0;
    virtual int Recv(std::string* out) = 0;
    virtual int Send(const std::string& in) = 0;
    virtual void Close() = 0;
public:

    void BuildTcpServer(uint16_t port)
    {
        SocketOrDie();
        BindOrDie(port);
        ListenOrDie(default_backlog);
    }

    void BuildTcpClient(std::string &server_ip, uint16_t server_port)
    {
        SocketOrDie();
        ConnectOrDie(server_ip,server_port);
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket(int sockfd = default_sockfd)
        : _sockfd(sockfd)
    {
    }
    virtual void Close() override
    {
        if (_sockfd != default_sockfd)
            ::close(_sockfd); // ::表示调用 全局作用域 中的 close 函数
    }
    virtual void SocketOrDie() override
    {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error!";
            exit(SOCKET_ERROR);
        }
        LOG(LogLevel::INFO) << "socket success, socket: " << _sockfd;
    }

    virtual void BindOrDie(uint16_t port) override
    {
        InetAddr local(port);
        int n = ::bind(_sockfd, CONST_CONV(local.Addr()), local.AddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error!";
            exit(BIND_ERROR);
        }
        LOG(LogLevel::INFO) << "bind success, socket: " << _sockfd;
    }

    virtual void ListenOrDie(int backlog) override
    {
        int n = ::listen(_sockfd, default_backlog);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error!";
            exit(LISTEN_ERROR);
        }
        LOG(LogLevel::INFO) << "listen success, sockfd: " << _sockfd;
    }

    virtual void ConnectOrDie(std::string &server_ip, uint16_t server_port) override
    {
        InetAddr server(server_ip, server_port);
        int n = ::connect(_sockfd, CONST_CONV(server.Addr()), server.AddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "connect error!";
            exit(CONNECT_ERROR);
        }
        LOG(LogLevel::INFO) << "connect success, sockfd: " << _sockfd;
    }

    virtual std::shared_ptr<Socket> Accept(InetAddr* client) override
    {
        std::cout << std::endl;

        struct sockaddr_in addr;
        socklen_t len = sizeof(addr);
        int fd = ::accept(_sockfd, CONV(addr), &len);
        if (fd < 0)
        {
            LOG(LogLevel::WARNING) << "accept failed";
            return nullptr;
        }
        client->SetAddr(addr);
        LOG(LogLevel::INFO) << "accept success, client: " << client->StringAddr();

        return std::make_shared<TcpSocket>(fd); // 这个server的sockfd就可以调用Recv和Send方法。
    }

    virtual int Recv(std::string* out) override
    {
        char buf[1024*8];
        ssize_t n = ::recv(_sockfd,buf,sizeof(buf)-1,0);
        if(n > 0)
        {
            buf[n] = 0;
            *out += buf; // += 可能要不断的读 
        }

        return n;
    }

    virtual int Send(const std::string& in) override
    {
        return ::send(_sockfd,in.c_str(),in.size(),0);
    }

private:
    int _sockfd; // 既可以是listen_sockfd，也可以是sockfd，复用代码。
};
